2 * wiggle - apply rejected patches
4 * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5 * Copyright (C) 2010 Neil Brown <neilb@suse.de>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * Email: <neilb@suse.de>
27 * Wiggle is a tool for working with patches that don't quite apply properly.
28 * It provides functionality similar to 'diff' and 'merge' but can
29 * work at the level of individual words thus allowing the merging of
30 * two changes that affect the same line, but not the same parts of that line.
32 * Wiggle can also read patch and merge files. Unlike 'merge' it does not
33 * need to be given three separate files, but can be given a file and a patch
34 * and it will extract the pieces of the two other files that it needs from
37 * Wiggle performs one of three core function:
38 * --extract -x extract part of a patch or merge file
39 * --diff -d report differences between two files
40 * --merge -m merge the changes between two files into a third file
42 * This is also a --browse (-B) mode which provides interactive access
45 * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
46 * I can get these from individual files, from a diff (unified or context) or
50 * If one file is given, it is a merge file (output of 'merge').
51 * If two files are given, the second is assumed to be a patch,
52 * the first is a normal file.
53 * If three files are given, they are taken to be normal files.
56 * If one file is given, it is a patch
57 * If two files are given, they are normal files.
60 * Only one file can be given. -p indicates it is a patch,
61 * otherwise it is a merge.
62 * One of the flags -1 -2 or -3 must also be given and they indicate which
63 * part of the patch or merge to extract.
65 * Difference calculation and merging is performed on lines (-l) or words (-w).
66 * Each 'word' is either 1/all alphnumeric (or '_'), 2/ all space or tab,
67 * or 3/ any other single character.
69 * In the case of -w, an initial diff is computed based on non-trivial words
70 * which includes alhpanumeric words and newlines.
72 * This diff is computed from the ends of the file and is used to find
73 * a suitable starting point and range. Then a more precise diff is
74 * computed over that restricted range
76 * Other options available are:
77 * --replace -r replace first file with result of merge.
78 * --help -h provide help
79 * --version -v version
81 * Defaults are --merge --words
98 fprintf(stderr, "%s: fatal error\n", Cmd);
103 void *xmalloc(int size)
105 void *rv = malloc(size);
107 char *msg = "Failed to allocate memory - aborting\n";
108 write(2, msg, strlen(msg));
114 void printword(FILE *f, struct elmnt e)
117 fprintf(f, "%.*s", e.len, e.start);
120 sscanf(e.start+1, "%d %d %d", &a, &b, &c);
121 fprintf(f, "*** %d,%d **** %d\n", b, c, a);
125 static void printsep(struct elmnt e1, struct elmnt e2)
127 int a, b, c, d, e, f;
128 sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
129 sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
130 printf("@@ -%d,%d +%d,%d @@\n", b, c, e, f);
133 static int extract(int argc, char *argv[], int ispatch, int which)
135 /* extract a branch of a diff or diff3 or merge output
138 struct stream f, flist[3];
142 "%s: no file given for --extract\n", Cmd);
147 "%s: only give one file for --extract\n", Cmd);
150 f = load_file(argv[0]);
151 if (f.body == NULL) {
153 "%s: cannot load file '%s' - %s\n", Cmd,
154 argv[0], strerror(errno));
158 if (split_patch(f, &flist[0], &flist[1]) == 0) {
160 "%s: No chunk found in patch: %s\n", Cmd,
165 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
167 "%s: merge file %s looks bad.\n", Cmd,
172 if (flist[which-'1'].body == NULL) {
174 "%s: %s has no -%c component.\n", Cmd,
178 if (write(1, flist[which-'1'].body,
179 flist[which-'1'].len)
180 != flist[which-'1'].len)
186 static int do_diff_lines(struct file fl[2], struct csl *csl)
191 while (a < fl[0].elcnt || b < fl[1].elcnt) {
193 if (fl[0].list[a].start[0]) {
200 } else if (b < csl->b) {
201 if (fl[1].list[b].start[0]) {
209 if (fl[0].list[a].start[0] == '\0')
210 printsep(fl[0].list[a],
219 if (a >= csl->a+csl->len)
226 static int do_diff_words(struct file fl[2], struct csl *csl)
230 int sol = 1; /* start of line */
232 while (a < fl[0].elcnt || b < fl[1].elcnt) {
242 for (a1 = a; a1 < csl->a ; a1++)
243 if (ends_line(fl[0].list[a1])) {
249 for (; a < csl->a ; a++) {
250 printword(stdout, fl[0].list[a]);
251 if (ends_line(fl[0].list[a])) {
264 printword(stdout, fl[0].list[a]);
265 sol = ends_line(fl[0].list[a]);
267 } while (a < csl->a);
268 printf("%s-->>>", sol ? "|" : "");
271 } else if (b < csl->b) {
276 for (b1 = b; b1 < csl->b; b1++)
277 if (ends_line(fl[1].list[b1])) {
283 for (; b < csl->b ; b++) {
284 printword(stdout, fl[1].list[b]);
285 if (ends_line(fl[1].list[b])) {
298 printword(stdout, fl[1].list[b]);
299 sol = ends_line(fl[1].list[b]);
301 } while (b < csl->b);
302 printf("%s++>>>", sol ? "|" : "");
309 for (a1 = a; a1 < csl->a+csl->len; a1++)
310 if (ends_line(fl[0].list[a1]))
313 if (fl[0].list[a].start[0]) {
315 for (; a < csl->a+csl->len; a++, b++) {
316 printword(stdout, fl[0].list[a]);
317 if (ends_line(fl[0].list[a])) {
323 printsep(fl[0].list[a], fl[1].list[b]);
330 printword(stdout, fl[0].list[a]);
331 if (ends_line(fl[0].list[a]))
336 if (a >= csl->a+csl->len)
343 static int do_diff(int argc, char *argv[], int obj, int ispatch,
344 int which, int reverse)
346 /* create a diff (line or char) of two streams */
347 struct stream f, flist[3];
348 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
355 fprintf(stderr, "%s: no file given for --diff\n", Cmd);
358 f = load_file(argv[0]);
359 if (f.body == NULL) {
361 "%s: cannot load file '%s' - %s\n", Cmd,
362 argv[0], strerror(errno));
366 split_patch(f, &flist[0], &flist[1]);
367 if (!flist[0].body || !flist[1].body) {
369 "%s: couldn't parse patch %s\n", Cmd,
375 flist[0] = load_file(argv[0]);
376 if (flist[0].body == NULL) {
378 "%s: cannot load file '%s' - %s\n", Cmd,
379 argv[0], strerror(errno));
383 f = load_file(argv[1]);
384 if (f.body == NULL) {
386 "%s: cannot load patch"
388 argv[1], strerror(errno));
393 split_patch(f, &flist[2],
397 split_patch(f, &flist[1],
401 flist[1] = load_file(argv[1]);
402 if (flist[1].body == NULL) {
404 "%s: cannot load file"
406 argv[1], strerror(errno));
412 "%s: too many files given for --diff\n", Cmd);
420 fl[0] = split_stream(flist[0], obj == 'l' ? ByLine : ByWord);
421 fl[1] = split_stream(flist[1], obj == 'l' ? ByLine : ByWord);
422 if (chunks2 && !chunks1)
423 csl = pdiff(fl[0], fl[1], chunks2);
425 csl = diff(fl[0], fl[1]);
428 printf("@@ -1,%d +1,%d @@\n",
429 fl[0].elcnt, fl[1].elcnt);
430 exit_status = do_diff_lines(fl, csl);
433 /* count lines in each file */
436 for (i = 0 ; i < fl[0].elcnt ; i++)
437 if (ends_line(fl[0].list[i]))
439 for (i = 0 ; i < fl[1].elcnt ; i++)
440 if (ends_line(fl[1].list[i]))
442 printf("@@ -1,%d +1,%d @@\n", l1, l2);
444 exit_status = do_diff_words(fl, csl);
449 static int do_merge(int argc, char *argv[], int obj,
450 int reverse, int replace, int ignore, int show_wiggles,
453 /* merge three files, A B C, so changed between B and C get made to A
455 struct stream f, flist[3];
458 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
459 char *replacename = NULL, *orignew = NULL;
460 struct csl *csl1, *csl2;
462 FILE *outfile = stdout;
466 fprintf(stderr, "%s: no files given for --merge\n", Cmd);
471 for (i = 0; i < argc; i++) {
472 flist[i] = load_file(argv[i]);
473 if (flist[i].body == NULL) {
474 fprintf(stderr, "%s: cannot load file '%s' - %s\n",
476 argv[i], strerror(errno));
482 fprintf(stderr, "%s: too many files given for --merge\n",
487 case 1: /* a merge file */
489 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
490 fprintf(stderr, "%s: merge file %s looks bad.\n",
496 case 2: /* a file and a patch */
498 chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]);
500 case 3: /* three separate files */
509 for (i = 0; i < 3; i++) {
510 if (flist[i].body == NULL) {
511 fprintf(stderr, "%s: file %d missing\n", Cmd, i);
517 replacename = xmalloc(strlen(argv[0]) + 20);
518 orignew = xmalloc(strlen(argv[0]) + 20);
519 strcpy(replacename, argv[0]);
520 strcpy(orignew, argv[0]);
521 strcat(orignew, ".porig");
522 if (open(orignew, O_RDONLY) >= 0 ||
524 fprintf(stderr, "%s: %s already exists\n",
529 strcat(replacename, "XXXXXX");
530 fd = mkstemp(replacename);
533 "%s: could not create temporary file for %s\n",
538 outfile = fdopen(fd, "w");
542 fl[0] = split_stream(flist[0], ByLine);
543 fl[1] = split_stream(flist[1], ByLine);
544 fl[2] = split_stream(flist[2], ByLine);
546 fl[0] = split_stream(flist[0], ByWord);
547 fl[1] = split_stream(flist[1], ByWord);
548 fl[2] = split_stream(flist[2], ByWord);
550 if (chunks2 && !chunks1)
551 csl1 = pdiff(fl[0], fl[1], chunks2);
553 csl1 = diff(fl[0], fl[1]);
554 csl2 = diff(fl[1], fl[2]);
556 ci = print_merge2(outfile, &fl[0], &fl[1], &fl[2],
557 csl1, csl2, obj == 'w',
558 ignore, show_wiggles);
559 if (!quiet && ci.conflicts)
561 "%d unresolved conflict%s found\n",
563 ci.conflicts == 1 ? "" : "s");
564 if (!quiet && ci.ignored)
566 "%d already-applied change%s ignored\n",
568 ci.ignored == 1 ? "" : "s");
572 if (rename(argv[0], orignew) == 0 &&
573 rename(replacename, argv[0]) == 0)
577 "%s: failed to move new file into place.\n",
582 return (ci.conflicts > 0);
585 static int multi_merge(int argc, char *argv[], int obj,
586 int reverse, int ignore, int show_wiggles,
587 int replace, int strip,
599 "%s: -p in merge mode requires -r\n",
605 "%s: -p in merge mode requires exactly one file\n",
610 f = fopen(filename, "r");
612 fprintf(stderr, "%s: cannot open %s\n",
616 pl = parse_patch(f, NULL, &num_patches);
618 if (set_prefix(pl, num_patches, strip) == 0) {
619 fprintf(stderr, "%s: aborting\n", Cmd);
622 for (i = 0; i < num_patches; i++) {
625 asprintf(&name, "_wiggle_:%d:%d:%s",
626 pl[i].start, pl[i].end, filename);
629 rv |= do_merge(2, av, obj, reverse, 1, ignore,
630 show_wiggles, quiet);
635 int main(int argc, char *argv[])
645 int verbose = 0, quiet = 0;
649 int show_wiggles = 0;
653 trace = getenv("WIGGLE_TRACE");
657 while ((opt = getopt_long(argc, argv,
658 short_options, long_options,
659 &option_index)) != -1)
665 helpmsg = HelpExtract;
674 helpmsg = HelpBrowse;
677 fputs(helpmsg, stderr);
681 fputs(Version, stderr);
686 fputs(Usage, stderr);
698 "%s: mode is '%c' - cannot set to '%c'\n",
704 if (obj == 0 || obj == opt) {
709 "%s: cannot select both words and lines.\n", Cmd);
730 if (which == 0 || which == opt) {
735 "%s: can only select one of -1, -2, -3\n", Cmd);
738 case 'p': /* 'patch' or 'strip' */
740 strip = atol(optarg);
755 vpatch(argc-optind, argv+optind, ispatch,
756 strip, reverse, replace);
757 /* should not return */
761 if (obj && mode == 'x') {
763 "%s: cannot specify --line or --word with --extract\n",
767 if (mode != 'm' && !obj)
769 if (replace && mode != 'm') {
771 "%s: --replace only allowed with --merge\n", Cmd);
774 if (mode == 'x' && !which) {
776 "%s: must specify -1, -2 or -3 with --extract\n", Cmd);
779 if (mode != 'x' && mode != 'd' && which) {
781 "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
786 if (ispatch && which == '3') {
788 "%s: cannot extract -3 from a patch.\n", Cmd);
794 exit_status = extract(argc-optind, argv+optind, ispatch, which);
797 exit_status = do_diff(argc-optind, argv+optind, obj, ispatch, which, reverse);
801 exit_status = multi_merge(argc-optind,
808 exit_status = do_merge(argc-optind, argv+optind, obj, reverse, replace,
809 ignore, show_wiggles, quiet);